Embed LDH's RDF-defined blocks as atomic RDFa islands#16
Merged
Conversation
Blocks defined purely in RDF (ldh:View, ldh:ResultSetChart, ldh:Object) can now live wherever the content model admits a div, per the LDH v6 document format: a div[@about][@typeof] block element with its definition triples as span[@Property] children (objects via @resource, literals as span text) - no wrappers, no rdf:_N sequences, no document-to-block containment edges. The editor treats such a div as an atomic island: never a text host, focusable/navigable/deletable like a block image (shared local:select-island doctrine, undo/redo chords included), a hard boundary for merges (merge-host-in gains a tail-island guard - islands hold no hosts, the lookup would leapfrog them) and for the delete machine (clamped-range moves boundaries out of islands, so they join selections whole and are only ever removed whole), and byte-identical on round-trip: the visual rendering is injected into an ephemeral div[data-role=rendering] child by the overridable mode="local:render-island" hook and stripped at canonicalization. The core (new src/blocks.xsl: $object-block-types param, the local:island predicate, the render hook + neutral card, extension stubs) knows no LDH vocabulary. The LDH extension (src/ldh-blocks.xsl, layered by the src/ldh-editor.xsl entry via xsl:import) supplies the class list, async renderers - ixsl:promise/ixsl:http-request chains fetching the referenced documents by content negotiation on clean trailing-slash URIs (no file extensions anywhere; serve.mjs replaces python http.server and negotiates rdf+xml / sparql-results+xml representations) - and the Block... insert dialog wired into the slash menu and toolbar through the new hook points. In production LinkedDataHub, client.xsl plays the extension's role, bridging the hook into its block rendering (MIGRATION.md par. 12). Two build flavors: the core SEF (dist/index.xsl.sef.json) is what GitHub Pages deploys - Pages cannot content-negotiate, so the LDH-extended SEF (dist/ldh-editor.xsl.sef.json) and the demo/ showcase page stay local-only. New coverage: extractor/canonical/lint fixtures for the storage form, tests/browser/blocks.mjs against tests/fixture-blocks.html (hydration, round-trip, navigation, boundaries, dialog insertion top-level and nested, stage-2/sweep atomicity, canonical copy, undo); fixture-nesting.html runs the extended SEF so every core suite doubles as proof the extension layers non-invasively. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The plan shipped in 0496acf (src/blocks.xsl, src/ldh-blocks.xsl, src/ldh-editor.xsl, demo/, tests, CLAUDE.md docs); the file is no longer needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…blocks # Conflicts: # CLAUDE.md # tests/browser/run.mjs
…blocks Reconciles the object-block islands with master's nested drag-and-drop, exclusive-c14n Source view and output downloads: - undo.xsl: restore-snapshot re-converges chrome via local:ensure-chrome (nested handles included), then re-fires the island renderer for islands captured mid-render. - Nested islands are draggable blocks and carry their own handles now; blocks.mjs invariants and expectations updated to the draggable-block chrome model (ported from invariants.mjs I3). - demo/index.html and tests/fixture-blocks.html load lib/xml-c14n-sync.js, required by the c14n Source view. - run.mjs runs both new suites (dragnest + blocks); CLAUDE.md weaves both sides' feature descriptions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The v6 document format retires the v5 ldh:Object + rdf:value indirection, and in XHTML+RDFa the last reason for it is gone: placement is structural, so embedding an existing resource needs no node at all. A reference block is an empty div whose @about is the referenced resource's own absolute URI - naming the resource IS the reference, and the empty div extracts to zero triples (no scaffolding statements in the content graph). - blocks.xsl: local:reference-block (effectively-empty div, absolute @about naming a different document than the page - string tests only, safe in the hot island predicate), OR-ed into local:island; neutral card labels typeless islands 'Resource'. - ldh-blocks.xsl: ldh:Object dropped from $object-block-types; the reference renderer dereferences @about by conneg (DBpedia works directly: CORS + 303 to RDF/XML; label picks no-lang/en; property table capped at 10); dialog kinds are now Resource (one absolute URI straight into @about, gated by local:is-absolute-iri) / View / Chart with per-kind fragment id; literal definition spans no longer dropped on insert (filter checked @content, values live in text content per the v6 format). - demo embeds Ada Lovelace by her DBpedia URI, hydrated live in the browser. - fixture-blocks.html gains a third island (a stored reference block, its absolute @about stamped by the fixture script); blocks.mjs covers reference init/round-trip/copy and inserts a nested reference via the dialog. - MIGRATION.md par. 12 and the wiki page define the idiom; the ac:mode trade-off is documented (per-embed modes need a typed block). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Islands embed external objects - their rendered cards paint real tables (chart results, a reference's property card), and the drop resolver's cell shortcut had no ephemera guard: pointing a dragged block at a rendered td resolved the drop INTO the rendering div, where the next canonicalization would silently delete it. Only content cells qualify now - a td inside ephemera or an island interior is external rendering, never a drop zone; the pointer falls through to the island itself, so drops land before/after it and islands are never drop receivers. blocks.mjs adds a real-gesture regression test (verified failing against the unguarded resolver): dragging a paragraph onto the chart's rendered table cell lands it adjacent to the island, never inside. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Blocks defined purely in RDF —
ldh:View,ldh:ResultSetChart— can now be embedded wherever the content model admits a block, per the LDH v6 document format: adiv[@about][@typeof]element with its definition triples asspan[@property]children (objects via@resource, literals as span text). Embedding an existing resource needs no node at all: a reference block is an emptydivwhose@aboutis the resource's own absolute URI (the demo embedshttp://dbpedia.org/resource/Ada_Lovelacedirectly — the v5ldh:Object+rdf:valueindirection is retired, per the updated wiki spec). No wrappers, nordf:_Nsequences, no document→block containment edges — extraction yields the block descriptions (a reference block extracts to zero triples), containment is implicit in the content.The editor treats such a block as an atomic island:
tabindex="-1"makes it a focusable navigation island (shared doctrine with block images: arrow-key selection, Backspace/Delete removes it whole, undo/redo chords work while it's focused);local:merge-host-ingains a tail-island guard) and for the cross-host delete machine (local:clamped-rangemoves boundaries out of islands, so a selection takes them whole or not at all);div[data-role=rendering]child by the overridablemode="local:render-island"hook and stripped at canonicalization; the extractor skips it.Core / extension split
src/blocks.xsl(core, zero LDH knowledge):$object-block-typesparam (default empty), thelocal:island()predicate (typed islands ORlocal:reference-block()— an effectively-empty div with an absolute non-document@about), the render hook + neutral card, and extension stubs for dialogs / toolbar buttons / slash-menu items.src/ldh-blocks.xsl+ entrysrc/ldh-editor.xsl(xsl:importlayering, higher import precedence): the LDH class list, async renderers —ixsl:promise/ixsl:http-requestchains fetching the referenced documents by content negotiation on clean trailing-slash URIs (no file extensions anywhere) — and the "Block…" insert dialog wired into the slash menu and toolbar. In production LinkedDataHub, client.xsl plays this module's role, bridging the hook into its block rendering (v6ldh:Block/ v5ldh:RenderRow— MIGRATION.md §12).Build & deployment
Two SEFs: the core
dist/index.xsl.sef.jsonis what GitHub Pages deploys (Pages can't content-negotiate); the LDH-extendeddist/ldh-editor.xsl.sef.jsonpowers the local/demo/showcase and the browser fixtures.make upnow serves vianode serve.mjs, which negotiatesapplication/rdf+xml/application/sparql-results+xmlrepresentations of document URIs.Test plan
make test— 50 headless PASS, incl. new extractor/canonical/lint fixtures for the storage formmake test-browser— all 14 suites green, incl. newblocks.mjs(43 assertions: hydration, view-source round-trip, island navigation/deletion/boundaries, dialog insertion top-level and nested in a list item, stage-2/sweep delete atomicity, canonical copy, undo + invariants after every mutation)fixture-nesting.htmlruns the extended SEF, so every existing suite doubles as proof the extension layers without disturbing core behaviormake up→/demo/hydrates a chart over conneg and a nested reference block live from DBpedia (CORS + 303 to RDF/XML); Source shows the clean v6 placeholders; Extract RDF shows the block descriptions🤖 Generated with Claude Code